#pragma once

#define emit
#define slots
#define signals public
#define connect(sender, signal, slot) ((sender)->signal.bind(slot))

template<typename... Args>
class Slot
{
public:
	using OnFunc = std::function<void(Args&&...)>;

	Slot(OnFunc func)
		:m_func(func) {}

	void Exec(Args&&... args)
	{
		m_func(std::forward<Args>(args)...);
	}

private:
	OnFunc m_func = nullptr;
};

template<typename... Args>
class Signal
{
public:
	using SlotPtr = std::shared_ptr<Slot<Args&&...>>;
	using OnFunc = std::function<void(Args&&...)>;

	void bind(OnFunc func)
	{
		m_slotVec.push_back(SlotPtr(new Slot<Args&&...>(func)));
	}

	void operator() (Args&&... args)
	{
		for (auto iter : m_slotVec)
		{
			iter->Exec(std::forward<Args>(args)...);
		}
	}

private:
	std::vector<SlotPtr> m_slotVec;
};

///////////////////////
class testSignal
{
public:
	void start()
	{
		emit m_s1();
		emit m_s2("i am a string signal.");
		emit m_s3(10, "i am a int(10) and string signal");
	}
signals:
	Signal<> m_s1;
	Signal<std::string> m_s2;
	Signal<int, std::string> m_s3;
};

class testSlot
{
public slots:
	void func1()
	{
		std::cout << "func1" << std::endl;
	}

	void func2(const std::string& str)
	{
		std::cout << "func2" << str << std::endl;
	}

	void func3(int n, const std::string& str)
	{
		std::cout << "func3" << n << str << std::endl;
	}
};